#include "CmdSocket.h"

#ifdef __ANDROID__
#include <android/log.h>
#define BOSE_LOGI(...) __android_log_print(ANDROID_LOG_INFO,  "BOSECMD_SOCK", __VA_ARGS__)
#define BOSE_LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "BOSECMD_SOCK", __VA_ARGS__)
#define BOSE_LOGW(...) __android_log_print(ANDROID_LOG_WARN,  "BOSECMD_SOCK", __VA_ARGS__)
#define BOSE_LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "BOSECMD_SOCK", __VA_ARGS__)
#else
#define BOSE_LOGI(str, ...) do{printf("BOSECMD_SOCK ");printf(str,##__VA_ARGS__);printf("\n");}while(0)
#define BOSE_LOGD(str, ...) do{printf("BOSECMD_SOCK ");printf(str,##__VA_ARGS__);printf("\n");}while(0)
#define BOSE_LOGW(str, ...) do{printf("BOSECMD_SOCK ");printf(str,##__VA_ARGS__);printf("\n");}while(0)
#define BOSE_LOGE(str, ...) do{printf("BOSECMD_SOCK ");printf(str,##__VA_ARGS__);printf("\n");}while(0)
#endif

using namespace com::bose;

CmdSocket::CmdSocket(SOCKIDTYPE id) :mId(id){}

CmdSocket::~CmdSocket() {
    isExiting = 1;
    if (serverFd > 0) {
        close(serverFd);
        serverFd = -1;
    }
}

CmdSocketServer::CmdSocketServer(SOCKIDTYPE id, CommandProcessorFuncType handler, int flag):
          CmdSocket(id), mHandler(handler), mDefaultFlag(flag){
    BOSE_LOGI("CmdSocketServer Constructor, flag=%d, bind port:%d", flag, id);
    mAm = new AudioMessager();
}

int CmdSocketServer::service_listen() {
    int ret = 0;
#ifdef USING_UNIX_DOMAIN_SOCKET
    unlink(mId);  //remove old file
    serverFd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un local;
    memset(&local, 0, sizeof(struct sockaddr_un));
    local.sun_family = AF_UNIX;
    strcpy(local.sun_path, mId);

#elif defined(USING_INET_SOCKET)
    serverFd = socket(AF_INET,SOCK_STREAM, 0);
    struct sockaddr_in local;
    local.sin_family = AF_INET;
    local.sin_addr.s_addr = htonl(INADDR_ANY);
    local.sin_port = htons(mId);
#endif
    int reuse = 1;
    ret = setsockopt( serverFd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse) );

    ret = bind(serverFd, (struct sockaddr*)&local, sizeof(local));
    if (ret < 0)  {
        BOSE_LOGE("bind() failed, serverFd=%d", serverFd);
        return ret;
    }

#ifdef USING_UNIX_DOMAIN_SOCKET
    ret = chmod(mId, BOSE_CMD_SOCKET_MODE);
#endif

    ret = listen(serverFd, MAX_BOSECMD_LISTEN_CLIENT_Fd_NUM);
    BOSE_LOGD("listen() Fd>> %d <<<  status=%d", serverFd, ret);
    if (ret < 0) {
        return ret;
    }
    return ret;
}

void* exec_thread(void* arg) {
    int ret = 0;
    int sockFd = ((threadArgType*)arg)->socketFd;
    CommandProcessorFuncType handler = ((threadArgType*)arg)->handler;
    CmdSocketServer* _this = 
        (CmdSocketServer*)(((threadArgType*)arg)->pThis);
    while (ret >= 0) {
        ret = _this->exec_send_once(sockFd, handler);
        usleep(12*1024);
        //if (! ( _this->getFlag() & O_NONBLOCK)) close(sockFd);
    }
    return (void*)(long)ret;
}

int CmdSocketServer::proccess_play(const char * p, char* const outData, int maxBuffSize) {
    int ret = -1;
    if (nullptr == p || 0 ==strlen(p)) {
        ;
    } else if (strstr(p, "PLAY ")) {
        const std::string s(p + strlen("PLAY "));
        BOSE_LOGW("play >> %s", s.c_str());
        session_id id = mAm->play_wav(s);
        sprintf(outData, "%d", id);
        ret = 0;
    } else if (strstr(p, "STOP ")) {
        const std::string s(p + strlen("STOP "));
        session_id id = stoi(s);
        BOSE_LOGW("try to stop session %d", id);
        ErrorCode status = mAm->stop_wav(id);
        strcpy(outData,  error2str(status));
        ret = 0;
    } else if (strstr(p, "PAUSE ")) {
        const std::string s(p + strlen("PAUSE "));
        session_id id = stoi(s);
        BOSE_LOGD("try to pause session %d", id);
        ErrorCode status = mAm->pause_wav(id, true);
        strcpy(outData, error2str(status));
        ret = 0;
    } else if (strstr(p, "RESUME ")) {
        const std::string s(p + strlen("RESUME "));
        session_id id = stoi(s);
        BOSE_LOGD("try to resume session %d", id);
        ErrorCode status = mAm->pause_wav(id, false);
        strcpy(outData, error2str(status));
        ret = 0;
    } else if (strstr(p, "SEEK ")) {
        const std::string s(p + strlen("SEEK "));
        int32_t seconds = stoi(s);
        BOSE_LOGD("seek to second %d", seconds);
        ErrorCode status = mAm->seek_to(seconds);
        strcpy(outData, error2str(status));
        ret = 0;
    }
    return ret;
}

int CmdSocketServer::exec_send_once(int clientFd, CommandProcessorFuncType handleFunc) {
    int ret = 0, size = -1;
    char recvBuf[MAX_CMD_LEN + 1];
    char* respBuf;
    memset(recvBuf, 0, sizeof(recvBuf));
    size = CmdSocket::receive_data(clientFd, recvBuf, MAX_CMD_LEN);
    if(0 == size) {
        BOSE_LOGW("client %d read 0 size data, exit.", clientFd);
        if (! ( _this->getFlag() & O_NONBLOCK)) {
            close(clientFd);
        }
        return ERROR_SOCKET_CLOSED;
    } else if(size > 0) {
        respBuf = (char*)malloc(MAX_BUFF_SIZE + 1);
        if(NULL == respBuf) return -3;
        memset(respBuf, 0, MAX_BUFF_SIZE + 1);
        respBuf[0] = (char)clientFd;  // Used to print the fd in process function
        ret = proccess_play(recvBuf, respBuf, MAX_BUFF_SIZE);
        if (ret < 0) {
            ret = handleFunc(recvBuf, respBuf, MAX_BUFF_SIZE);
        }
        if(strlen(respBuf) == 0){
            sprintf(respBuf,"%d", ret);  //use function ret as response data
        }
        if (strlen(respBuf) > 0 && strlen(respBuf) < 255) {
            BOSE_LOGI("response to FD:%d, str=%s", clientFd, respBuf);
        }
        ret = CmdSocket::send_data(clientFd, respBuf, strlen(respBuf));
        if (ret < 0) {
            close(clientFd);
        }
        free(respBuf);
    } else if(size < 0) {
        BOSE_LOGE("Error[%d] when receiving Data:%s.\n", errno, strerror(errno));
        ret  = ERROR_SOCKET_CLOSED;
    }
    return ret;
}

int CmdSocket::send_data(int fd, const char* buffer, int len) {
    int ret;
    ret = send(fd, buffer, len, 0);
    if (ret < 0) {
        BOSE_LOGE("send() to [%d] failed", fd);
    }
    return ret;
}

int CmdSocket::receive_data(int fd, const char * buffer, int max_len) {
    return recv(fd, (void*)buffer, max_len, 0);
}

int CmdSocketServer::processing() {
    int clientFd = -1;
    pthread_t tid;

    while (!isExiting) {
        clientFd = accept(get_service_fd(), NULL, NULL);
        BOSE_LOGD("accept() fd:%d ,flag=%d", clientFd, mDefaultFlag);
        if (clientFd > 0 && NULL != mHandler) {
#ifdef BOSECMD_ALL_CMD_BLOCK
            exec_send_once(clientFd, mHandler);
#else
            threadArgType arg;
            arg.pThis = (void*)this;
            arg.socketFd = clientFd;
            arg.handler = mHandler;
            if (pthread_create(&tid, NULL, exec_thread, (void*)&arg)) {
                close(clientFd);
            }
#endif
        }
        usleep(12*1024);
    }
    return 0;
}